Skip to content

feat(cli): add interactive setup wizard and modernize init command#247

Merged
cameroncooke merged 14 commits intomainfrom
feat/setup-wizard
Mar 2, 2026
Merged

feat(cli): add interactive setup wizard and modernize init command#247
cameroncooke merged 14 commits intomainfrom
feat/setup-wizard

Conversation

@cameroncooke
Copy link
Collaborator

Add xcodebuildmcp setup — an interactive terminal wizard that configures project defaults (project/workspace, scheme, simulator, workflows, debug mode, Sentry opt-out) and persists results to .xcodebuildmcp/config.yaml.

Previously users had to hand-edit YAML to configure their project. The setup wizard discovers available projects, schemes, and simulators, presents them in a polished terminal UI via @clack/prompts, and writes a complete config file with a summary of what changed.

Supporting changes:

  • Shared Prompter abstraction (src/cli/interactive/prompts.ts) — wraps @clack/prompts with a testable interface that auto-selects defaults in non-interactive mode
  • sentryDisabled promoted to first-class config key — previously env-var-only (XCODEBUILDMCP_SENTRY_DISABLED), now settable in config.yaml and resolved through the standard config layering. A new hydrateSentryDisabledEnvFromProjectConfig() bridges the config file to the env var before Sentry init in all three entrypoints (cli, daemon, mcp server)
  • MCP tool refactors — extracted reusable discoverProjects(), listSchemes(), and listSimulators() functions from the MCP *Logic wrappers so both the MCP layer and CLI can call them directly. No behavior change to MCP tool responses
  • init command modernized — switched from raw readline to @clack/prompts, added interactive skill type and client selection when no flags are provided, replaced Cursor/Codex targets with generic "Agents Skills" target, added ~ expansion for --dest
  • persistProjectConfigPatch() — new utility for atomic config file updates with support for mutually exclusive key cleanup (projectPath vs workspacePath)

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 27, 2026

Open in StackBlitz

npm i https://pkg.pr.new/xcodebuildmcp@247

commit: ceed2e1

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Interactive Claude selection silently skipped for MCP skill
    • Added check for Claude in user-selected targets array to treat interactive selection as explicit intent, same as --client flag.
  • ✅ Fixed: Internal key filter misses camelCase logLevel variant
    • Added 'logLevel' to the internalKeys set to filter out the camelCase variant created by yargs camel-case-expansion.
  • ✅ Fixed: Inconsistent TTY check in ensureAgentsGuidance after refactor
    • Changed guard from process.stdin.isTTY to isInteractiveTTY() to match the check used by promptConfirm function.

Create PR

Or push these changes by commenting:

@cursor push 3034395277
Preview (3034395277)
diff --git a/src/cli/commands/init.ts b/src/cli/commands/init.ts
--- a/src/cli/commands/init.ts
+++ b/src/cli/commands/init.ts
@@ -286,7 +286,7 @@
   }
 
   if (!force) {
-    if (!process.stdin.isTTY) {
+    if (!isInteractiveTTY()) {
       throw new Error(
         `${AGENTS_FILE_NAME} exists and requires confirmation to update. Re-run with --force to apply the change in non-interactive mode.`,
       );
@@ -665,6 +665,11 @@
     return { allowedTargets: targets, skippedClients: [] };
   }
 
+  const hasClaudeTarget = targets.some((t) => t.id === 'claude');
+  if (clientFlag === 'claude' || hasClaudeTarget) {
+    return { allowedTargets: targets, skippedClients: [] };
+  }
+
   const allowedTargets: ClientInfo[] = [];
   const skippedClients: Array<{ client: string; reason: string }> = [];
 
@@ -679,9 +684,5 @@
     allowedTargets.push(target);
   }
 
-  if (clientFlag === 'claude') {
-    return { allowedTargets: targets, skippedClients: [] };
-  }
-
   return { allowedTargets, skippedClients };
 }

diff --git a/src/cli/register-tool-commands.ts b/src/cli/register-tool-commands.ts
--- a/src/cli/register-tool-commands.ts
+++ b/src/cli/register-tool-commands.ts
@@ -162,7 +162,16 @@
 
       // Convert CLI argv to tool params (kebab-case -> camelCase)
       // Filter out internal CLI options before converting
-      const internalKeys = new Set(['json', 'output', 'style', 'socket', 'log-level', '_', '$0']);
+      const internalKeys = new Set([
+        'json',
+        'output',
+        'style',
+        'socket',
+        'log-level',
+        'logLevel',
+        '_',
+        '$0',
+      ]);
       const flagArgs: Record<string, unknown> = {};
       for (const [key, value] of Object.entries(argv as Record<string, unknown>)) {
         if (!internalKeys.has(key)) {
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@cameroncooke
Copy link
Collaborator Author

Re: investigation notes committed as project documentation — agreed, removed in 693a6f9. These were ephemeral investigation notes, not permanent documentation.

Add `xcodebuildmcp setup` — an interactive terminal wizard that walks
users through configuring project defaults (project/workspace, scheme,
simulator, workflows, debug mode, Sentry opt-out) and persists the
result to .xcodebuildmcp/config.yaml.

Key changes:
- New setup command with clack-based interactive prompts
- Shared Prompter abstraction for testable TTY/non-interactive prompts
- Promote sentryDisabled from env-var-only to first-class config key
- Extract reusable functions from discover_projs, list_schemes, list_sims
  so both MCP tools and CLI can call them directly
- Modernize init command to use clack prompts and interactive selection
- Replace Cursor/Codex client targets with generic Agents Skills target
- Add persistProjectConfigPatch for atomic config file updates
Extract shared isInteractiveTTY() to prompts.ts and add try/catch to
withSpinner so the spinner is stopped if the task throws, preventing
garbled terminal output on error.
…malizeLogLevel

Align internal log level naming with Sentry SDK conventions ('warn' instead
of 'warning'). Add normalizeLogLevel() to safely map external level strings
(including MCP protocol's 'warning') to internal LogLevel values.

Also removes the CLI daemon logLevel passthrough (debug flag no longer
overrides daemon log level) and filters 'log-level' from tool command args.
… debug

Pass existing project config into workflow option evaluation so that
debug-gated workflows (e.g. doctor) appear in the setup wizard when
the user's config already has debug: true.
Return structured JSON from init install/uninstall flows when no TTY is available.
Keep clack-based output for interactive sessions.

This gives agent and CI callers a stable machine-readable contract
without introducing additional flags or output modes.
Capture AGENTS guidance outcomes during non-interactive init and embed the status in the emitted JSON report. Include path and error details when AGENTS updates fail so automation can reason about partial success without parsing stderr.

Preserve existing failure behavior by throwing after JSON emission on AGENTS errors, and extend init command tests to cover created, updated, and error statuses in JSON output.
Preserve backwards compatibility for existing scripts that pass
--log-level warning while keeping help output focused on the
internal warn level token.

Map warning to warn during argument coercion in both the main
and lightweight yargs app builders so init/setup and normal CLI
execution behave consistently.
Delete AI-generated investigation log that was accidentally committed
as project documentation. The file contained stale line-number
references and phase-by-phase working notes, not permanent docs.
Add the same empty-options check to createNonInteractivePrompter's
selectOne that the TTY prompter already has, preventing an
out-of-bounds access if called with an empty array.
The --log-level option already coerces 'warning' to 'warn' for
backward compatibility, but --daemon-log-level was missing the
same coercer, causing a yargs validation error for users passing
the old value.
The setup wizard requires explicit --client or --dest in non-interactive
mode. Add --client auto to the test so it exercises the auto-detect
policy path, and emit skip reasons before the zero-targets error.
Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@cameroncooke cameroncooke merged commit d94b96a into main Mar 2, 2026
9 checks passed
@cameroncooke cameroncooke deleted the feat/setup-wizard branch March 2, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant